<?php
class Form {
	private $fields	= array();
	private $values	= array();
	private $errors	= array();

	private $name;
	private $action;
	private $method;
	private $submitTitle;

	private $validators = array();

	public function __construct ($name, $action = '', $method = 'post', $submitTitle = false, $populateWith = false, $validators = array()) {
		$this->name			= $name;
		$this->method		= $method == 'get' ? 'get' : 'post';
		$this->action		= $action;
		$this->submitTitle	= $submitTitle;

		if ($populateWith) {
			$this->populateWith($populateWith);
		}

		$commonValidators = array(
			'name'			=> '/^\S.*$/',											# name (atleast one character)
			'message'		=> '/^\S.*$/s',											# message (atleast one character)
			'dimensions'	=> '/^\d+x\d+$/',										# dimensions (DIGITxDIGIT)
			'price'			=> '/^\d+$/',											# price (atleast one digit)
			'url'			=> '/^http:\/\/?(www)?[^ ]+\.[^ ]{2,5}$/',				# url
			'email'			=> '/^.+?@.+?\..{2,4}$/',								# email
		);

		$commonValidators['title'] = $commonValidators['author'] = $commonValidators['name'];
		$commonValidators['comment'] = $commonValidators['description'] = $commonValidators['content'] = $commonValidators['message'];
		$commonValidators['website'] = $commonValidators['url'];

		$this->validators = array_merge($commonValidators, $validators);
	}

	public function populateWith ($values) {
		$this->values = array_merge($this->values, $values);
	}

	public function addField ($field) {
		$row = $this->method == 'get' ? $_GET : $_POST;

		$this->fields[] = array(
			'title'			=> isset($field['title']) ? $field['title'] : ucwords($field['name']), 
			'name'			=> $field['name'], 
			'value'			=> isset($this->values[$field['name']]) ? $this->values[$field['name']] : (isset($field['value']) ? $field['value'] : false), 
			'type'			=> isset($field['type']) ? $field['type'] : 'text', 
			'placeholder'	=> isset($field['placeholder']) ? $field['placeholder'] : false, 
			'autofocus'		=> isset($field['autofocus']) and $field['autofocus'] ? $field['autofocus'] : false, 
			'required'		=> isset($field['required']) and $field['required'] ? true : false,
			'options'		=> isset($field['options']) ? $field['options'] : false, 
			'checked'		=> (!isset($field['checked']) and isset($row[$field['name']]) ? true : (isset($field['checked']) and $field['checked'] ? true : false)), 
			'validation'	=> isset($field['validation']) ? $field['validation'] : $field['name']
		);
	}

	public function isSubmitted () {
		$row = $this->method == 'get' ? $_GET : $_POST;

		if (isset($row[$this->name])) {
			return $row;
		}

		return false;
	}

	public function validate ($checkSpam = false) {
		$row = $this->method == 'get' ? $_GET : $_POST;

	/*	if ($checkSpam and ($karma = SpamChecker::getKarma($row)) < 1) {
			$this->errors['__spam']			= 'The data in the form appears to be spam. Please remove overflow of links and spammy words.';
			$this->errors['__spam_info']	= SpamChecker::getInfo();
			$this->errors['__spam_karma']	= $karma;
		} */

		foreach ($this->fields as $field) {
			if ($field['required'] and (!isset($row[$field['name']]) or empty($row[$field['name']]))) {
				$this->errors[$field['name']] = 'Must not be empty';
			}
			elseif (($field['required'] or !empty($row[$field['name']])) and isset($this->validators[$field['validation']]) and !preg_match($this->validators[$field['validation']], $row[$field['name']])) {
				$this->errors[$field['name']] = 'Must be valid (' . $this->validators[$field['validation']] . ')';
			}
		}

		if (count($this->errors)) {
			return false;
		}

		return true;
	}

	public function setError ($error) {
		if (!isset($this->errors['__general'])) {
			$this->errors['__general'] = array();
		}

		$this->errors['__general'][] = $error;
	}

	public function setMessage ($message) {
		if (!isset($this->errors['__messages'])) {
			$this->errors['__messages'] = array();
		}

		$this->errors['__messages'][] = $message;
	}

	public function getErrors () {
		return $this->errors;
	}

	public function asHTML () {
		$html	= '';
		$fields	= $this->fields;

	/*	if (isset($this->errors['__spam'])) {
			$html .= "<p><strong>The data in the form appears to be spam. Please try with less URLs and/or spammy words. Here's what the SpamChecker says:</strong></p>";
			$html .= "<ul>";

			foreach ($this->errors['__spam_info'] as $spamInfo) {
				$html .= "<li>$spamInfo</li>";
			}

			$html .= "</ul>";
		} */

		if (isset($this->errors['__general'])) {
			foreach ($this->errors['__general'] as $generalError) {
				$html .= "<p class=\"error\"><strong>$generalError</strong></p>";
			}
		}

		if (isset($this->errors['__messages'])) {
			foreach ($this->errors['__messages'] as $message) {
				$html .= "<p class=\"success\"><strong>$message</strong></p>";
			}
		}

		$html .= "<form method=\"{$this->method}\" action=\"{$this->action}\">\n\t";

		# Add all fields
		foreach ($fields as $field) {
			if ($field['type'] != 'hidden') {
				$html .= "<p>\n\t\t";

				$label = "<label for=\"{$this->name}-{$field['name']}\">";

				if ($field['required']) {
					$label .= '<abbr title="Required Field">*</abbr> ';
				}

				$label .= "{$field['title']}</label>";

				$autofocus = $field['autofocus'] ? ' autofocus="autofocus"' : '';
				$placeholder = $field['placeholder'] ? " placeholder=\"{$field['placeholder']}\"" : '';
				$error = isset($this->errors[$field['name']]) ? ' class="error"' : '';
				$checked = (isset($field['checked']) and $field['checked']) ? ' checked="checked"' : '';

				switch ($field['type']) {
					# Textareas
					case 'textarea' : 
						$html .= "$label<br/>\n\t\t<br/><textarea rows=\"10\" cols=\"60\" name=\"{$field['name']}\" id=\"{$this->name}-{$field['name']}\"$placeholder$autofocus$error>{$field['value']}</textarea>";

						break;

					# Radios & checkboxes
					case 'checkbox' : 
					case 'radio' : 
						$html .= "<input type=\"{$field['type']}\" name=\"{$field['name']}\" id=\"{$this->name}-{$field['name']}\"$checked$autofocus$error/> $label";

						break;

					# Selects
					case 'select' : 
						$html .= "$label<br/>\n\t\t<select name=\"{$field['name']}\" id=\"{$this->name}-{$field['name']}\"$autofocus$error>";

						foreach ($field['options'] as $k => $v) {
							$html .= "\n\t\t\t<option value=\"$k\"";
							$html .= ($k == $field['value']) ? ' selected="selected"' : '';
							$html .= ">$v</option>";
						}

						$html .= "\n\t\t</select>";

						break;

					# Pass
					case 'password' : 
						$html .= "$label<br/>\n\t\t<input type=\"{$field['type']}\" name=\"{$field['name']}\" id=\"{$this->name}-{$field['name']}\" value=\"\"$placeholder$autofocus$error/>";

						break;

					# Other
					default : 
						$html .= "$label<br/>\n\t\t<input type=\"{$field['type']}\" name=\"{$field['name']}\" id=\"{$this->name}-{$field['name']}\" value=\"{$field['value']}\"$placeholder$autofocus$error/>";

						break;
				}

				# Errors
				if (isset($this->errors[$field['name']])) {
					$html .= "\n\t\t<br/>\n\t\t<strong>{$this->errors[$field['name']]}</strong>";
				}

				$html .= "\n\t</p>\n\n\t";
			}
		}

		# Add submit-button and hidden fields
		$html .= "<p>";

		foreach ($this->fields as $field) {
			if ($field['type'] == 'hidden') {
				$html .= "\n\t\t<input type=\"hidden\" name=\"{$field['name']}\" value=\"{$field['value']}\"/>";
			}
		}

		$html .= "\n\t\t<input type=\"hidden\" name=\"{$this->name}\" value=\"1\"/>";
		$html .= "\n\t\t<input type=\"submit\"";
		$html .= $this->submitTitle ? " value=\"{$this->submitTitle}\"" : '';
		$html .= '/>';
		$html .= "\n\t</p>\n\n</form>";

		return $html;
	}
}
